package xin.marcher.module.lifecycle.repository;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import xin.marcher.framework.common.util.data.DataCuttingUtil;
import xin.marcher.module.common.constants.construction.ProductConstants;
import xin.marcher.module.common.core.PageResult;
import xin.marcher.module.common.enums.YesOrNoEnum;
import xin.marcher.module.common.enums.construction.ProductExceptionCode;
import xin.marcher.module.common.enums.lifecycle.MarketExpriResultEnum;
import xin.marcher.module.common.exception.BaseBizException;
import xin.marcher.module.common.exception.CommonErrorCodeEnum;
import xin.marcher.module.common.exception.ProductBizException;
import xin.marcher.module.common.exception.ProductErrorCodeEnum;
import xin.marcher.module.lifecycle.converter.ItemExpriResultBufferConverter;
import xin.marcher.module.lifecycle.domain.dto.ItemExpriResultBufferDTO;
import xin.marcher.module.lifecycle.domain.dto.ItemPeriodStageDTO;
import xin.marcher.module.lifecycle.domain.dto.ProductExpriConfigurationDTO;
import xin.marcher.module.lifecycle.domain.dto.ProductExpriDataResultDTO;
import xin.marcher.module.lifecycle.domain.entity.ItemExpriResultBufferDO;
import xin.marcher.module.lifecycle.domain.entity.ItemExpriResultDO;
import xin.marcher.module.lifecycle.domain.request.ItemExpriResultBufferRequest;
import xin.marcher.module.lifecycle.mapper.ItemExpriResultBufferMapper;
import xin.marcher.module.lifecycle.mapper.ItemExpriResultMapper;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 考核结果资源管理
 */
@Slf4j
@Repository
public class ItemExpriResultRepository {

    /**
     * 考核结果Mapper
     */
    @Resource
    private ItemExpriResultMapper itemExpriResultMapper;

    /**
     * 考核结果缓冲Mapper
     */
    @Resource
    private ItemExpriResultBufferMapper itemExpriResultBufferMapper;

    /**
     * 商品考核资源管理
     */
    @Autowired
    private ProductExpriRepository productExpriRepository;

    @Resource
    private ItemExpriResultBufferConverter itemExpriResultBufferConverter;

    /**
     * 分页查询考核不通过数据集
     *
     * @param pageNum
     * @param pageSize
     * @return
     */
    public PageResult<ItemExpriResultBufferDO> queryFailExpriResultBufferByPage(Integer pageNum, Integer pageSize) {
        LambdaQueryWrapper<ItemExpriResultBufferDO> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(ItemExpriResultBufferDO::getMarketExpriResult, MarketExpriResultEnum.FAIL_PASS_EXAMINATION.getCode());
        queryWrapper.eq(ItemExpriResultBufferDO::getDelFlag, YesOrNoEnum.YES.getCode());
        Page<ItemExpriResultBufferDO> page = new Page<>(pageNum, pageSize);

        return new PageResult<>(itemExpriResultBufferMapper.selectPage(page, queryWrapper));
    }

    /**
     * 分页查询考核通过数据集
     *
     * @param pageNum
     * @param pageSize
     * @return
     */
    public PageResult<ItemExpriResultBufferDO> queryPassExpriResultBufferByPage(Integer pageNum, Integer pageSize) {
        LambdaQueryWrapper<ItemExpriResultBufferDO> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(ItemExpriResultBufferDO::getMarketExpriResult, MarketExpriResultEnum.PASS_EXAMINATION.getCode());
        queryWrapper.eq(ItemExpriResultBufferDO::getDelFlag, YesOrNoEnum.YES.getCode());
        Page<ItemExpriResultBufferDO> page = new Page<>(pageNum, pageSize);

        return new PageResult<>(itemExpriResultBufferMapper.selectPage(page, queryWrapper));
    }

    /**
     * 查询考核状态为通过和不通过的考核结果集合
     *
     * @return
     */
    public List<ItemExpriResultBufferDO> listItemExpriResultBuffer() {
        LambdaQueryWrapper<ItemExpriResultBufferDO> queryWrapper = Wrappers.lambdaQuery();

        // 查询条件，考核通过 或者 考核不通过的结果
        queryWrapper.eq(ItemExpriResultBufferDO::getMarketExpriResult, MarketExpriResultEnum.PASS_EXAMINATION.getCode())
                .or()
                .eq(ItemExpriResultBufferDO::getMarketExpriResult, MarketExpriResultEnum.FAIL_PASS_EXAMINATION.getCode());

        // 考核结果
        return itemExpriResultBufferMapper.selectList(queryWrapper);
    }

    /**
     * 删除缓冲表中的记录，避免下次重新计算
     *
     * @param id 考核结果缓冲表 主键
     */
    public Boolean deleteById(Long id) {
        LambdaUpdateWrapper<ItemExpriResultBufferDO> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(ItemExpriResultBufferDO::getId, id);
        updateWrapper.set(ItemExpriResultBufferDO::getDelFlag, YesOrNoEnum.YES.getCode());
        return itemExpriResultBufferMapper.update(null, updateWrapper) > 0;
    }


    /**
     * 累加失败次数
     *
     * @param expriData               考核指标数据
     * @param itemPeriodStage         商品生命周期数据
     * @param marketExpriResult       考核结果：0：考核中；1：考核通过；2：考核不通过；3：不考核或暂停考核
     * @param expriConfiguration      考核指标配置
     * @param conformingIntegrityFlag 是否是质量或诚信问题
     */
    public void incrFailTimes(ProductExpriDataResultDTO expriData,
                              ItemPeriodStageDTO itemPeriodStage,
                              Integer marketExpriResult,
                              ProductExpriConfigurationDTO expriConfiguration,
                              boolean conformingIntegrityFlag) {
        // 商品ID
        String itemId = expriData.getItemId();
        // 查询考核缓冲结果数据
        LambdaQueryWrapper<ItemExpriResultBufferDO> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(ItemExpriResultBufferDO::getItemId, itemId);
        ItemExpriResultBufferDO resultBuffer = itemExpriResultBufferMapper.selectOne(queryWrapper);
        // 考核缓冲结果数据不存在
        if (Objects.isNull(resultBuffer)) {
            // 失败次数设置为1
            ItemExpriResultBufferDO bufferEntity = buildExpriResultBufferDO(expriData, itemPeriodStage, marketExpriResult);
            itemExpriResultBufferMapper.insert(bufferEntity);
            return;
        }

        // 获取累加后的连续失败次数
        Integer failTimes = getFailTimes(resultBuffer.getFailTimes(), expriConfiguration.getFailTimes(), conformingIntegrityFlag);
        // 配置的最大连续失败次数
        Integer failTimesConfig = expriConfiguration.getFailTimes();
        // 考核是否失败
        if (failTimes >= failTimesConfig) {
            // 未通过考核
            completeExpriProcess(expriData, itemPeriodStage, MarketExpriResultEnum.FAIL_PASS_EXAMINATION.getCode());
            return;
        }

        // 更新失败次数到缓冲表
        LambdaUpdateWrapper<ItemExpriResultBufferDO> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(ItemExpriResultBufferDO::getItemId, itemId);
        updateWrapper.set(ItemExpriResultBufferDO::getFailTimes, failTimes);
        itemExpriResultBufferMapper.update(null, updateWrapper);
    }

    /**
     * 获取累加后的连续失败次数
     *
     * @param failTimes               未累加的连续失败次数
     * @param failTimesConfig         配置的最大连续失败次数
     * @param conformingIntegrityFlag 是否是质量或诚信问题
     * @return 累加后的连续失败次数
     */
    private Integer getFailTimes(Integer failTimes, Integer failTimesConfig, boolean conformingIntegrityFlag) {
        // 如果是质量或诚信问题
        if (conformingIntegrityFlag) {
            // 直接返回最大的连续失败次数，考核直接失败
            return failTimesConfig;
        }
        // 否则进行正常累加
        return ++failTimes;
    }

    /**
     * 考核结束处理
     * 考核结束，不管是白名单免考核、考核通过、考核不通过、还是单次考核未通过进行累加操作，都会调用这个方法进行处理
     * 白名单免考核，不需要处理累计的失败次数
     * 指标通过、指标不通过，需要清零失败次数
     *
     * @param expriData         考核指标数据
     * @param itemPeriodStage   商品生命周期数据
     * @param marketExpriResult 考核结果：0：考核中；1：考核通过；2：考核不通过；3：不考核或暂停考核
     */
    public void completeExpriProcess(ProductExpriDataResultDTO expriData, ItemPeriodStageDTO itemPeriodStage, Integer marketExpriResult) {
        // 缓冲表中记录是否存在
        LambdaQueryWrapper<ItemExpriResultBufferDO> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(ItemExpriResultBufferDO::getItemId, expriData.getItemId());
        ItemExpriResultBufferDO resultBuffer = itemExpriResultBufferMapper.selectOne(queryWrapper);
        // 构造考核结果缓冲表DO对象
        ItemExpriResultBufferDO bufferEntity = buildExpriResultBufferDO(expriData, itemPeriodStage, marketExpriResult);

        // 不存在则添加
        if (Objects.isNull(resultBuffer)) {
            // 写入考核结果缓冲数据
            itemExpriResultBufferMapper.insert(bufferEntity);
            // 将该考核指标数据设置为失效
            productExpriRepository.invalidExpriDataResult(expriData.getId());
            return;
        }
        // 更新考核结果缓冲
        LambdaUpdateWrapper<ItemExpriResultBufferDO> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(ItemExpriResultBufferDO::getItemId, bufferEntity.getItemId());
        itemExpriResultBufferMapper.update(bufferEntity, updateWrapper);
        // 本轮考核免考核，不删除考核指标数据，等待下一轮考核
        if (marketExpriResult.equals(MarketExpriResultEnum.NON_EXAMINATION.getCode())) {
            return;
        }
        // 将该考核指标数据设置为失效
        productExpriRepository.invalidExpriDataResult(expriData.getId());
    }


    /**
     * 构造考核结果缓冲数据DO
     *
     * @param expriData         考核指标数据
     * @param itemPeriodStage   商品生命周期数据
     * @param marketExpriResult 考核结果
     * @return
     */
    private ItemExpriResultBufferDO buildExpriResultBufferDO(ProductExpriDataResultDTO expriData, ItemPeriodStageDTO itemPeriodStage, Integer marketExpriResult) {
        ItemExpriResultBufferDO bufferEntity = new ItemExpriResultBufferDO();
        bufferEntity.setItemId(expriData.getItemId());
        bufferEntity.setCategoryId(expriData.getCategoryId());
        bufferEntity.setMarketExpriResult(marketExpriResult);
        bufferEntity.setPeriodStageId(itemPeriodStage.getProductPeriodId());

        // 指标通过或者指标未通过，连续失败次数都清0
        if (marketExpriResult.equals(MarketExpriResultEnum.PASS_EXAMINATION.getCode()) ||
                marketExpriResult.equals(MarketExpriResultEnum.FAIL_PASS_EXAMINATION.getCode())) {
            bufferEntity.setFailTimes(BigDecimal.ZERO.intValue());
        }
        // 单次考核未通过，初始化失败次数为1
        if (marketExpriResult.equals(MarketExpriResultEnum.UNDER_EXAMINATION.getCode())) {
            bufferEntity.setFailTimes(BigDecimal.ONE.intValue());
        }
        return bufferEntity;
    }

    /**
     * 将生命周期变更后的考核结果写入考核结果表
     *
     * @param itemExpriResultDO 考核结果数据
     */
    public void saveOrUpdateItemExpriResult(ItemExpriResultDO itemExpriResultDO) {
        if (Objects.isNull(itemExpriResultDO) || StringUtils.isEmpty(itemExpriResultDO.getItemId())) {
            throw new BaseBizException(ProductErrorCodeEnum.PARAM_CHECK_ERROR, ProductErrorCodeEnum.PARAM_CHECK_ERROR.getErrorCode());
        }

        LambdaQueryWrapper<ItemExpriResultDO> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(ItemExpriResultDO::getItemId, itemExpriResultDO.getItemId());
        ItemExpriResultDO selectResult = itemExpriResultMapper.selectOne(queryWrapper);
        itemExpriResultDO.initCommon();
        if (Objects.isNull(selectResult)) {
            // 新增
            itemExpriResultMapper.insert(itemExpriResultDO);
            return;
        }

        // 修改
        itemExpriResultDO.setId(selectResult.getId());
        itemExpriResultMapper.updateById(itemExpriResultDO);
    }

    /**
     * 分页查询出状态为考核中的考核缓冲结果数据中的连续失败次数集合
     *
     * @return 连续失败次数集合
     */
    public Map<String, Integer> listFailTimesByPage() {
        // 构造查询条件
        LambdaQueryWrapper<ItemExpriResultBufferDO> queryWrapper = Wrappers.lambdaQuery();
        // 考核状态为考核中
        queryWrapper.eq(ItemExpriResultBufferDO::getMarketExpriResult, MarketExpriResultEnum.UNDER_EXAMINATION.getCode());
        queryWrapper.eq(ItemExpriResultBufferDO::getDelFlag, YesOrNoEnum.YES.getCode());

        // 分页查询存放数据的总集合
        Map<String, Integer> results = new HashMap<>();

        int pageNum = 1;
        // 设置每次查询的数据量，最大为200
        int pageSize = ProductConstants.QUERY_ITEM_MAX_COUNT;
        Page<ItemExpriResultBufferDO> page = new Page<>(pageNum, pageSize);
        // 查询第一页数据
        Page<ItemExpriResultBufferDO> pageResult = itemExpriResultBufferMapper.selectPage(page, queryWrapper);

        // 判断是否还有数据，还有数据则页码+1继续执行分页查询
        List<ItemExpriResultBufferDO> batchResult = pageResult.getRecords();
        try {
            while (batchResult.size() >= ProductConstants.QUERY_ITEM_MAX_COUNT) {
                // 将分页查询出来的list数据中的failTimes属性添加到Map集合中，itemId作为key
                putAll(results, batchResult);
                pageNum += 1;
                page.setCurrent(pageNum);
                page = itemExpriResultBufferMapper
                        .selectPage(page, queryWrapper);
                batchResult = page.getRecords();
                // 每次循环获取数据后，休眠20ms，避免对数据库造成太大压力
                Thread.sleep(20);
            }
        } catch (InterruptedException e) {
            throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);
        }

        // 最后一组数据也放入结果集中
        putAll(results, batchResult);

        return results;
    }

    /**
     * 将list转换为Map，并将转换的Map添加到总Map中
     *
     * @param results     总Map集合
     * @param batchResult list数据
     */
    private void putAll(Map<String, Integer> results, List<ItemExpriResultBufferDO> batchResult) {
        Map<String, Integer> batchResultMap = batchResult.stream()
                .collect(Collectors.toMap(ItemExpriResultBufferDO::getItemId,
                        ItemExpriResultBufferDO::getFailTimes, (oldKey, newKey) -> oldKey));
        results.putAll(batchResultMap);
    }

    /**
     * 分页查询考核缓冲表中考核状态是考核中的数据
     *
     * @param itemIdList itemId集合
     * @return 考核数据Map，key为itemId
     */
    public Map<String, ItemExpriResultBufferDTO> listAssessingBufferResultByPage(List<String> itemIdList) {
        if (CollectionUtils.isEmpty(itemIdList)) {
            return CollectionUtils.newHashMap();
        }

        // 考核中的考核结果缓冲数据集合
        Map<String, ItemExpriResultBufferDTO> resultMap = new HashMap<>();

        // 一次最大查询200个数据，多个分页查询,这里做数据切割
        List<List<String>> splitList = DataCuttingUtil.dataCuttingString(itemIdList, ProductConstants.QUERY_ITEM_MAX_COUNT);
        for (List<String> itemIds : splitList) {
            LambdaQueryWrapper<ItemExpriResultBufferDO> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.in(ItemExpriResultBufferDO::getItemId, itemIds);
            queryWrapper.eq(ItemExpriResultBufferDO::getMarketExpriResult, MarketExpriResultEnum.UNDER_EXAMINATION.getCode());
            queryWrapper.eq(ItemExpriResultBufferDO::getDelFlag, YesOrNoEnum.YES.getCode());
            // 分批次查询出符合条件的考核结果缓冲数据集合
            List<ItemExpriResultBufferDO> assessingBufferResultList = itemExpriResultBufferMapper.selectList(queryWrapper);
            if (!CollectionUtils.isEmpty(assessingBufferResultList)) {
                Map<String, ItemExpriResultBufferDTO> result = assessingBufferResultList.stream()
                        .map(itemExpriResultBufferDO -> itemExpriResultBufferConverter.entityToDTO(itemExpriResultBufferDO))
                        .collect(Collectors.toMap(ItemExpriResultBufferDTO::getItemId,
                                itemExpriResultBuffer -> itemExpriResultBuffer, (oldKey, newKey) -> oldKey));
                resultMap.putAll(result);
            }
        }
        return resultMap;

    }

    /**
     * 修改考核结果缓冲表中的记录
     *
     * @param request
     */
    public void updateItemExpriResultBuffer(ItemExpriResultBufferRequest request) {
        LambdaUpdateWrapper<ItemExpriResultBufferDO> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(ItemExpriResultBufferDO::getItemId, request.getItemId());
        updateWrapper.set(ItemExpriResultBufferDO::getMarketExpriResult, request.getMarketExpriResult());
        updateWrapper.set(ItemExpriResultBufferDO::getFailTimes, request.getFailTimes());

        itemExpriResultBufferMapper.update(null, updateWrapper);
    }

    /**
     * 批量插入考核缓冲结果数据
     *
     * @param insertRequest
     */
    public void insertBatch(List<ItemExpriResultBufferRequest> insertRequest) {
        List<ItemExpriResultBufferDO> bufferEntityList = itemExpriResultBufferConverter.listRequestToEntity(insertRequest);
        if (CollectionUtils.isEmpty(bufferEntityList)) {
            return;
        }

        Integer count = itemExpriResultBufferMapper.insertBatch(bufferEntityList);
        if (count <= 0) {
            throw new ProductBizException(CommonErrorCodeEnum.SQL_ERROR);
        }
    }

    /**
     * 批量写入到考核结果表中
     *
     * @param list
     */
    public void saveItemExpriResultBatch(List<ItemExpriResultDO> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        int count = itemExpriResultMapper.saveBatch(list);
        if (count <= 0) {
            throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);
        }
    }

    /**
     * 批量设置考核结果buffer数据为失效状态
     *
     * @param list
     */
    public void batchDeleteExpriBuffer(List<ItemExpriResultBufferDO> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        int count = itemExpriResultBufferMapper.batchDelete(list, YesOrNoEnum.NO.getCode());
        if (count <= 0) {
            throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);
        }
    }

    /**
     * 批量设置考核结果及其供需状态数据为失效状态
     *
     * @param list
     */
    public void batchDeleteExpriResult(List<String> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        int count = itemExpriResultMapper.batchUpdate(list, YesOrNoEnum.YES.getCode(), YesOrNoEnum.NO.getCode());
        if (count < 0) {
            throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);
        }
    }
}
